package com.uh.rds.tester.data;

import com.uh.console.domain.vo.RdsServiceNodesVo;
import com.uh.rds.testing.config.TestConfig;
import com.uh.rds.testing.config.TestConfigManager;
import com.uh.rds.testing.conn.RdsConnectInfo;
import com.uh.console.client.ConsoleWebClient;
import com.uh.console.client.RdsServiceHelper;
import com.uh.rds.testing.data.FileTask;
import com.uh.rds.testing.data.TestDataGenerator;
import com.uh.rds.testing.utils.RdsConnectionUtils;
import com.uh.rds.testing.validator.DataValidatorArguments;
import com.uh.rds.testing.validator.DataValidatorResult;
import com.uh.rds.testing.validator.DataValidatorRunner;
import com.uh.rds.testing.validator.types.*;
import org.junit.jupiter.api.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;

import static com.uh.rds.testing.config.TestConfigManager.getConsoleClientConf;
import static com.uh.rds.testing.base.DataValidatorStep.*;

/**
 * 主从环境下的数据过期测试
 * 测试环境：
 *  - 一个测试登录开启的控制台。
 *  - 具备至少1个中心节点。
 *  - 在控制台中建立 1主1从的集群模式RDS服务。
 *  - 配置要求，关闭节点的备份存盘（cfg.xml中Server.DataDump设置为0),
 * 测试case说明：
 * 1. 先插入8万条过期时间是8秒的key.
 * 2. 等待7秒后，再插入12万条数据(不带过期时间)插入时段要跨8秒的过期时间点，然后验证并删除这12万条数据。
 * 3. 验证之前插入的过期时间是8秒的key，是否已经被正确过期删除掉了。
 * 注意：测试过程要测试两遍，一遍不一定会出问题！
 */
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MasterSlaveExpireTest {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    private ConsoleWebClient client;
    private RdsServiceNodesVo serviceNodesVo;
    private RdsConnectInfo connection;
    private TestConfig conf;
    private DataValidatorRunner tester;
    private DataValidatorRunner expTester;

    File dataBase = new File("data/MasterSlaveExpireTest");
    File stringDataFile = new File(dataBase, "string-data.csv");
    File listDataFile = new File(dataBase, "list-data.csv");

    File stringExpDataFile = new File(dataBase, "string-exp-data.csv");
    File listExpDataFile = new File(dataBase, "list-exp-data.csv");

    /**
     * 初始化测试变量
     *  - 读取测试配置到 conf
     *  - 获取被测试的 RDS服务 信息
     */
    @BeforeAll
    public void init() {
        logger.info("MasterSlaveExpireTest init ...");
        conf = TestConfigManager.getTestConfig("MasterSlaveExpireTest");
        client = ConsoleWebClient.getInstance(getConsoleClientConf());
        client.login();

        Long testServiceId = conf.getLong("testServiceId");
        serviceNodesVo = RdsServiceHelper.getServiceInfo(testServiceId, client);
        connection = RdsConnectionUtils.getRdsConnection(serviceNodesVo);

        DataValidatorArguments config = new DataValidatorArguments();
        config.setThreadCount(conf.getInteger("threadCount"));
        config.setValidateInterval(conf.getLong("validateInterval"));
        config.setValidateFromSlave(true);
        tester = new DataValidatorRunner(config);
        tester.addValidator(StringDataValidator.class, stringDataFile);
        tester.addValidator(ListDataValidator.class, listDataFile);


        DataValidatorArguments expConfig = new DataValidatorArguments();
        expConfig.setThreadCount(conf.getInteger("threadCount"));
        expConfig.setValidateInterval(conf.getLong("validateInterval"));
        expConfig.setExpireSeconds(conf.getInteger("expireSeconds"));
        expConfig.setValidateFromSlave(true);
        expTester = new DataValidatorRunner(expConfig);
        expTester.addValidator(StringDataValidator.class, stringExpDataFile);
        expTester.addValidator(ListDataValidator.class, listExpDataFile);

        System.out.println("expireSeconds: " + expConfig.getExpireSeconds());
    }

    /**
     * 生成测试数据，第一次生成后之后可重复使用。
     * 会判断数据文件是否存在，如果存在则不再生成。
     */
    @Test
    @Order(1)
    public void generateTestData() {
        int dataCount = conf.getInteger("dataCount");
        int expireDataCount = conf.getInteger("expireDataCount");
        int subDataCount = conf.getInteger("subDataCount");
        if(stringDataFile.exists() == false) {
            logger.info("Generating test data ...");

            TestDataGenerator dataGenerator = new TestDataGenerator();
            dataGenerator.addTask(new FileTask(stringDataFile, "string", dataCount, 1));
            dataGenerator.addTask(new FileTask(listDataFile, "list", dataCount, subDataCount));

            dataGenerator.addTask(new FileTask(stringExpDataFile, "string", expireDataCount, 1));
            dataGenerator.addTask(new FileTask(listExpDataFile, "list", expireDataCount, subDataCount));

            int executed = dataGenerator.executeTasks();
            Assertions.assertEquals(4, executed);
        } else {
            logger.info("Test data already exists, skip generating ...");
        }
    }



    @Test
    @Order(2)
    public void testRound1() throws Exception {
        RdsConnectionUtils.flushDb(connection);
        testDataExpired();
    }


    @Test
    @Order(3)
    public void testRound2() throws Exception {
        testDataExpired();
    }


    private void testDataExpired() throws Exception {

        long expireMillis = conf.getInteger("expireSeconds") * 1000;

        expTester.prepareThreadsData(connection);
        tester.prepareThreadsData(connection);

        //RdsServiceHelper.flushDb(connection);

        //1. 插入过期数据
        DataValidatorResult result1 = expTester.runTest(PROCESS, EXPIRE);
        System.out.println("Expire data" + result1.summary());
        Assertions.assertTrue(result1.isPassed());

        Thread.sleep(expireMillis - 800);

        //2. 非过期数据的验证测试
        DataValidatorResult result = tester.runTest(PROCESS, VALIDATE, CLEAR);
        System.out.println("Average data" + result.summary());
        Assertions.assertTrue(result.isPassed());

        Thread.sleep(800);

        //3. 验证过期数据是否都已经删除
        DataValidatorResult result2 = expTester.runTest(VALIDATE_EXPIRED);
        System.out.println("Expire data" +result2.summary());
        Assertions.assertTrue(result2.isPassed());
    }


    @Test
    public void deleteDataFiles() {
        logger.info("Deleting test data files...");
        stringDataFile.deleteOnExit();
        listDataFile.deleteOnExit();

        stringExpDataFile.deleteOnExit();
        listExpDataFile.deleteOnExit();
    }

}
